SPB Git

Repository Public

page.tsx 5.1 KB 92 lines tsx

1import type { Metadata } from "next";2import { PageIntro } from "@/components/ui/Section";3import { research } from "@/lib/data";4import { formatDate, getDict, type Locale } from "@/lib/i18n";5import { pageMetadata } from "@/lib/seo";67type Props = { params: Promise<{ locale: Locale }> };89export async function generateMetadata({ params }: Props): Promise<Metadata> {10  return pageMetadata((await params).locale, "research", "/research");11}1213const TONE = { completed: "text-ok border-ok/30 bg-ok/10", ongoing: "text-warn border-warn/30 bg-warn/10", proposed: "text-accent border-accent/30 bg-accent-soft" } as const;1415export default async function ResearchPage({ params }: Props) {16  const { locale } = await params;17  const t = getDict(locale);18  const r = t.research;19  const groups = (["completed", "ongoing", "proposed"] as const).map((status) => ({ status, items: research.filter((e) => e.status === status) })).filter((g) => g.items.length > 0);20  return (21    <>22      <PageIntro eyebrow={r.eyebrow} title={r.title} intro={r.intro} />23      <div className="shell">24        <nav aria-label={r.eyebrow} className="flex flex-wrap gap-2 border-y border-line py-5">25          {groups.map((g) => (26            <a key={g.status} href={`#${g.status}`} className={`rounded-full border px-3.5 py-1.5 font-mono text-[0.6875rem] uppercase tracking-[0.1em] ${TONE[g.status]}`}>{t.common.status[g.status]} · {g.items.length}</a>27          ))}28        </nav>29        {groups.map((g) => (30          <section key={g.status} id={g.status} aria-labelledby={`${g.status}-h`} className="scroll-mt-24 pt-16 md:pt-24">31            <h2 id={`${g.status}-h`} className="h2">{t.common.status[g.status]}</h2>32            <ol className="mt-8 divide-y divide-line border-y border-line">33              {g.items.map((e) => (34                <li key={e.id} className="reveal grid gap-5 py-10 md:grid-cols-12 md:gap-8">35                  <div className="md:col-span-3">36                    <p className="label">{t.common.kinds[e.kind]}</p>37                    <p className="num mt-2 font-mono text-[0.8125rem] text-body"><time dateTime={e.date}>{formatDate(e.date, locale)}</time></p>38                    <ul className="mt-4 flex flex-wrap gap-1.5">39                      {e.themes.map((th) => <li key={th} className="rounded-full border border-line px-2.5 py-0.5 text-[0.75rem] text-mute">{t.common.themes[th as keyof typeof t.common.themes] ?? th}</li>)}40                    </ul>41                  </div>42                  <article className="md:col-span-9">43                    <h3 className="max-w-[46ch] text-[1.375rem] font-medium leading-snug tracking-[-0.02em]" lang={e.reference ? "en" : undefined}>{e.reference ? e.title.en : e.title[locale]}</h3>44                    {e.reference && locale === "fr" ? <p className="mt-2 max-w-[60ch] text-[0.9375rem] italic text-mute">{e.title.fr}</p> : null}45                    <p className="prose-tech mt-5">{e.summary[locale]}</p>46                    <div className="mt-6 grid gap-6 border-t border-line pt-5 sm:grid-cols-2">47                      <div>48                        <h4 className="label">{r.clusterUse}</h4>49                        <p className="mt-2 text-[0.9375rem] leading-relaxed text-body">{e.clusterUse[locale]}</p>50                      </div>51                      <div>52                        <h4 className="label">{r.reference}</h4>53                        {e.reference ? (54                          <p className="mt-2 text-[0.9375rem] leading-relaxed text-body">55                            {e.reference.authors} ({e.reference.year}). <cite className="not-italic" lang="en">{e.title.en}</cite>. {e.reference.series}, {e.reference.label}.{" "}56                            <a className="link whitespace-nowrap" href={e.reference.url} rel="noopener">{r.read}</a>57                          </p>58                        ) : (59                          <p className="mt-2 text-[0.9375rem] leading-relaxed text-mute">{r.noReference}</p>60                        )}61                      </div>62                    </div>63                  </article>64                </li>65              ))}66            </ol>67          </section>68        ))}6970        <section aria-labelledby="stack-h" className="grid gap-8 pt-20 md:grid-cols-12 md:pt-28">71          <div className="md:col-span-3"><h2 id="stack-h" className="eyebrow">{r.stack.title}</h2></div>72          <div className="md:col-span-9">73            <p className="prose-tech">{r.stack.intro}</p>74            <dl className="mt-6 divide-y divide-line border-y border-line">75              {r.stack.items.map((it) => (76                <div key={it.k} className="grid gap-2 py-4 sm:grid-cols-[12rem_1fr] sm:gap-6">77                  <dt className="label">{it.k}</dt>78                  <dd className="text-[0.9375rem] leading-relaxed text-body">{it.v}</dd>79                </div>80              ))}81            </dl>82          </div>83        </section>84        <section aria-labelledby="own-h" className="grid gap-8 pt-16 md:grid-cols-12 md:pt-20">85          <div className="md:col-span-3"><h2 id="own-h" className="eyebrow">{r.ownership.title}</h2></div>86          <p className="prose-tech md:col-span-9">{r.ownership.body}</p>87        </section>88      </div>89    </>90  );91}92
Files main
Menu

Theme